import * as THREE from "three";
import * as CANNON from "cannon-es";
import _ from 'lodash';

import { Space } from "../enums/Spaces";
import { SimulationFrame } from "../physics/spring_simulation/SimulationFrame";


export function setDefaults ( options: {}, defaults: {} ) {
    return _.defaults( {}, _.cloneDeep( options ), defaults );
}

export function setupMeshProperties ( child: THREE.Mesh<THREE.BufferGeometry, THREE.MeshStandardMaterial> ) {
    child.castShadow = true;
    child.receiveShadow = true;


    if ( child.material.map !== null ) {
        let mat = new THREE.MeshPhongMaterial();
        mat.shininess = 0;
        mat.name = child.material.name;
        mat.map = child.material.map;
        mat.map.anisotropy = 4;
        mat.aoMap = child.material.aoMap;
        mat.transparent = child.material.transparent;
        //@ts-ignore
        mat.skinning = child.material.skinning;
        // mat.map.encoding = THREE.LinearEncoding;
        //@ts-ignore
        child.material = mat;
    }
}

export function cannonVector ( vec: THREE.Vector3 ) {
    return new CANNON.Vec3( ...vec );
}

export function cannonQuat ( quat: THREE.Quaternion ): CANNON.Quaternion {
    return new CANNON.Quaternion( ...quat );
}

/**@description 获得模型的世界矩阵或者本地矩阵 */
export function getMatrix ( object: THREE.Object3D, space: Space ): THREE.Matrix4 {
    switch ( space ) {
        case Space.Local: return object.matrix;
        case Space.Global: return object.matrixWorld;
    }
}

export function getUp ( object: THREE.Object3D, space = Space.Global ): THREE.Vector3 {
    const matrix = getMatrix( object, space );
    const elements = matrix.elements;
    return new THREE.Vector3(
        elements[ 4 ],
        elements[ 5 ],
        elements[ 6 ],
    );
}

export function getForward ( object: THREE.Object3D, space = Space.Global ): THREE.Vector3 {
    const matrix = getMatrix( object, space );
    const elements = matrix.elements;
    return new THREE.Vector3(
        -elements[ 8 ],
        -elements[ 9 ],
        -elements[ 10 ],
    );
}

export function getBack ( object: THREE.Object3D, space = Space.Global ): THREE.Vector3 {
    const matrix = getMatrix( object, space );
    const elements = matrix.elements;
    return new THREE.Vector3(
        elements[ 8 ],
        elements[ 9 ],
        elements[ 10 ],
    );
}

export function getRight ( object: THREE.Object3D, space = Space.Global ): THREE.Vector3 {
    const matrix = getMatrix( object, space );
    const elements = matrix.elements;
    return new THREE.Vector3(
        elements[ 0 ],
        elements[ 1 ],
        elements[ 2 ],
    );
}

export function spring ( source: number, dest: number, velocity: number, mass: number, damping: number ): SimulationFrame {

    let acceleration = dest - source;
    acceleration /= mass;
    velocity += acceleration;
    velocity *= damping;

    const position = source + velocity;

    return new SimulationFrame( position, velocity );

}

export function springV ( source: THREE.Vector3, dest: THREE.Vector3, velocity: THREE.Vector3, mass: number, damping: number ): void {

    const acceleration = new THREE.Vector3().subVectors( dest, source );
    acceleration.divideScalar( mass );
    velocity.add( acceleration );
    velocity.multiplyScalar( damping );
    source.add( velocity );

}